home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Archives / ForCLI / ccd_3_2.lha / ccd.c < prev    next >
C/C++ Source or Header  |  1993-07-04  |  16KB  |  697 lines

  1. /*
  2.  * ccd.c
  3.  */
  4.  
  5. /* $Id: ccd.c,v 1.24 1993/07/03 18:41:52 beust Exp $ */
  6.  
  7. static char __version[] = "\0$VER: ccd 3.2 (03-Jul-93)";
  8.  
  9. #define EXTRAHELP "ccd 3.2, by Cédric BEUST (beust@sophia.inria.fr) $Date: 03-Jul-93\n\n"
  10.  
  11. #include <stdio.h>
  12. #include <assert.h>
  13. #include <string.h>
  14. #include <proto/dos.h>
  15. #include <proto/intuition.h>
  16. #include <libraries/dos.h>
  17. #include <libraries/dosextens.h>
  18. #include <libraries/amigaguide.h>
  19. #include <intuition/intuition.h>
  20. #include <clib/dos_protos.h>
  21. #include <clib/exec_protos.h>
  22. #include <clib/graphics_protos.h>
  23. #include <clib/amigaguide_protos.h>
  24.  
  25. #include "ccd.h"
  26.  
  27. #define GRAPHICS
  28.  
  29. #define min(a,b) (a < b ? a : b)
  30.  
  31. #ifndef NEW
  32. #define NEW(v, t) v = (t *) malloc(sizeof(*v))
  33. #endif
  34.  
  35. #define D(x) x
  36.  
  37. #define IntuitionBase gv -> IntuitionBase
  38.  
  39. /****************************************************************************/
  40. /* Private functions */
  41. /****************************************************************************/
  42.  
  43. static Entry
  44. locateDir(struct GlobalVars *gv, char *dir, DataBase db, BOOL partial)
  45. /* Return the path for the fragment given, if any, in database. partial  */
  46. /* is true if only the partial dir must be returned */
  47. {
  48.   Entry result = NULL, entry = NULL;
  49.   int match = 0;
  50.   char *p, *onedir, pat1[128], pat3[128];
  51.   char p1[300], p3[300];
  52.  
  53.   sprintf(pat1, "#?(/|:)%s(/|)", dir);
  54.   sprintf(pat3, "#?%s#?", dir);
  55.   ParsePatternNoCase(pat1, p1, 300);
  56.   ParsePatternNoCase(pat3, p3, 300);
  57. /* Give priority to "exact matches" (e.g. DH0:l instead of DH0:Applications) */
  58. /* match = 1 if approximate match, 2 if exact match */
  59.   while (! DB_EndOfDataBase(db) && match != 2) {
  60.     int i;
  61.     entry = DB_NextEntry(db);
  62.     p = entry -> fullPath;
  63.     if (MatchPatternNoCase(p1, p)) {
  64.       match = 2;
  65.       result = entry;
  66.     }
  67.     else if (MatchPatternNoCase(p3, p)) {
  68.       match = (partial ? 2 : 1);
  69.       result = entry;
  70.     }
  71.   }
  72.  
  73.   return result;
  74. }
  75.  
  76. static BPTR
  77. getLock(struct GlobalVars *gv, char *name, long code)
  78. {
  79.   BPTR *locklist = gv -> lockList;
  80.   int lockpt = gv -> lockpt;
  81.  
  82.   locklist[lockpt] = Lock(name, code);
  83.   if (locklist[lockpt])
  84.     return locklist[lockpt++];
  85.   else
  86.     return 0;
  87. }
  88.  
  89. static void
  90. unlock(BPTR lock)
  91. {
  92. /*
  93.   UnLock(lock);
  94. */
  95. }
  96.  
  97. /*
  98. static void
  99. freeAllLocks()
  100. {
  101.   int i;
  102.   for (i = 0; i < lockpt; i++)
  103.     UnLock(locklist[i]);
  104. }
  105. */
  106.  
  107. static char *
  108. reverse(char *s)
  109. /* Return the string s backwards (modify s)*/
  110. {
  111.   register char c;
  112.   char *result = s + strlen(s) - 1, *r2 = s;
  113.  
  114.   while (result > s) {
  115.     c = *result;
  116.     *result = *s;
  117.     *s = c;
  118.     s++; result--;
  119.   }
  120.   return(r2);
  121. }
  122.  
  123. #ifdef A
  124. int
  125. mystricmp(char *bigdir, char *shortdir)
  126. /* Check if shortdir is part of the path bigdir, return 0 if it is */
  127. {
  128.   char t[50], *pt = t, *ps;
  129.   int i = 0;
  130.  
  131.   while (*bigdir) {
  132.     while (*bigdir && *bigdir != ':' && *bigdir != '/' && *bigdir != '\n')
  133.       *pt++ = *bigdir++;
  134.     if (*bigdir) bigdir++;
  135.     *pt++ = '\0';
  136.     pt = t;
  137.   }
  138.  
  139. /* Now, we must see if shortdir is included in t */
  140.  
  141.   for (i = 0; i <= strlen(t) - strlen(shortdir); i++) {
  142.     pt = &t[i]; ps = shortdir;
  143.     while (*pt && *ps && (*pt | 0x20) == (*ps | 0x20)) {
  144.       ps++; pt++;
  145.     };
  146.     if (*pt == 0 || *ps == 0) return(0);   /* match! */
  147.   }
  148.   return(1);
  149. }
  150. #endif
  151.  
  152. static void
  153. myputs(char *s)
  154. {
  155.   Write(Output(), s, strlen(s));
  156. }
  157.  
  158.  
  159. static void
  160. readDirs(char *dir, FILE *f)
  161. {
  162.   BPTR lock;
  163.   struct FileInfoBlock info, *fib;
  164.  
  165.   lock = Lock(dir, ACCESS_READ);
  166.   if (lock == 0) {
  167.     fprintf(stderr,"*** Yup! Couldn't lock %s\n", dir);
  168.     return;
  169.   }
  170.   
  171.   fib = (struct FileInfoBlock *) Examine(lock, &info);
  172.   fib = &info;
  173.   while (1) {
  174.     if (ExNext(lock, fib) == 0) break;
  175.     if (fib -> fib_DirEntryType > 0) {
  176.       char t[50],c;
  177.       strcpy(t, dir);
  178.       if ((c = t[strlen(t) - 1]) != ':' && c != '/')
  179.         strcat(t,"/");
  180.       strcat(t, fib -> fib_FileName);
  181.       fprintf(f, "%s\n", t);
  182.       readDirs(t,f);
  183.     }
  184.   }
  185.   unlock(lock);
  186. }
  187.  
  188.  
  189. static int
  190. writeConfigFile(struct GlobalVars *gv, char **dirname, int ndirs, char *mode)
  191. /* Update the config file with the list of dirs (dirname) of length ndirs */
  192. {
  193.   char *file = gv -> prefs.configFile;
  194.   FILE *f = fopen(file, mode);
  195.   char *tempFile = "t:.cdconfig.unsorted";
  196.   char command[128];
  197.   int i;
  198.  
  199.   if (f == NULL) {
  200.     fprintf(stderr, "*** Couldn't open '%s (%s)', maybe an assign is missing?\n",
  201.         file, mode);
  202.     return(0);
  203.   } 
  204.   printf("%s with", file);
  205.   for (i = 0; i < ndirs; i++) {
  206.     printf(" %s", *dirname);
  207.     fflush(stdout);
  208.     fprintf(f, "%s\n", *dirname);
  209.     readDirs(*dirname++, f);
  210.   }
  211.   printf(" -- done!\n");
  212.   fclose(f);
  213.  
  214.  
  215.   sprintf(command, "copy %s %s", file, tempFile);
  216.   if (SystemTags(command, TAG_DONE) == 0) {
  217.     DeleteFile(file);
  218.     sprintf(command, "sort %s %s", tempFile, file);
  219.     if (SystemTags(command, TAG_DONE) != 0) {
  220.       fprintf(stderr, "*** couldn't sort %s %s\n", tempFile, file);
  221.       sprintf(command, "copy %s %s", tempFile, file);
  222.     }
  223.   }
  224.   else {
  225.     fprintf(stderr, "*** couldn't copy %s to %s\n", file, tempFile);
  226.   }
  227. }
  228.  
  229. /****************************************************************************/
  230. /* Public functions */
  231. /****************************************************************************/
  232.  
  233. int
  234. updateConfigFile(struct GlobalVars *gv, char **dirname, int ndirs)
  235. {
  236.   printf("Updating ");
  237.   return writeConfigFile(gv, dirname, ndirs, "a");
  238. }
  239.  
  240. int
  241. createConfigFile(struct GlobalVars *gv, char **dirname, int ndirs)
  242. {
  243.   printf("Creating ");
  244.   return writeConfigFile(gv, dirname, ndirs, "w");
  245. }
  246.  
  247.  
  248. void
  249. updatePrompt(char *currentdir)
  250. /* Update the concerned field with the new current dir                 */
  251. /* This routine is for users of wshell or such, thar display this name */
  252. /* as the shell prompt.                                                */
  253. /* This trick was previously pointed to me by Henry J. Cobb on Usenet  */
  254. /* for my 'find' program (another great utility of mine :-)).          */
  255. /* Let him be thanked again!                                           */
  256. {
  257.   struct Process *pr = (struct Process *) FindTask(0L);
  258.   struct CommandLineInterface *cli = (struct CommandLineInterface *) pr -> pr_CLI;
  259.   char *p;
  260.   int n;
  261.  
  262.   n = (int) cli;
  263.   n = n << 2;
  264.   cli = (struct CommandLineInterface *) n;
  265.  
  266.   n = (int) cli -> cli_SetName;
  267.   n = n << 2;
  268.   p = (char *) n;
  269.  
  270.   *p++ = strlen(currentdir);          /* it is a BSTR, so length first */
  271.   while (*currentdir) *p++ = *currentdir++;  /* don't add '\0' */
  272. }
  273.  
  274. #ifdef A
  275. void
  276. getRealName(struct GlobalVars *gv, BPTR lock, char *result)
  277. /* This is a very useful function! Put in result path of lock */
  278. /* Assume lock is not null */
  279. /* Result always device:dir/dir/...   */
  280. {
  281.   char *p = result;
  282.   struct FileInfoBlock ib;  
  283.  
  284.   assert(lock);
  285.  
  286.   if (gv -> prefs.noExpand) return;
  287.   Examine(lock, &ib);
  288.   strcpy(result, reverse(ib.fib_FileName));
  289.   strcat(result, "/");
  290.   while (lock) {
  291.     lock = ParentDir(lock);
  292.     if (lock) {
  293.       Examine(lock, &ib);
  294.       strcat(result, reverse(ib.fib_FileName));
  295.       strcat(result,"/");
  296.       unlock(lock);
  297.     }
  298.   }
  299.   p = &p[strlen(p) - 1];
  300.   *p-- = '\0';
  301.   while (*p != '/') p--;
  302.   *p = ':';
  303.   reverse(result);
  304. }
  305. #endif
  306.  
  307. void
  308. changeDir(struct GlobalVars *gv, char *dir, int occ)
  309. /* The main function to change to the fragment of dir given,  */
  310. /* to the occ'th (first = 1) occurence found in the config file */
  311. /* New 1.3: first, try to cd right into 'dir' */
  312. {
  313.   char actualdir[256];
  314.   BPTR lock;
  315.  
  316.   DB_Rewind(gv -> db);
  317.  
  318.   strcpy(actualdir, dir);
  319.   if ((lock = Lock(actualdir, ACCESS_READ))) {   /* can we cd directly? */
  320.     BPTR old;
  321.     NameFromLock(lock, actualdir, 256);
  322.     lock = getLock(gv, actualdir, ACCESS_READ);
  323.     if (gv -> prefs.verbose)
  324.       printf("Current directory is now %s\n", actualdir);
  325.     SetCurrentDirName(actualdir);
  326.     old = CurrentDir(lock);                     /* yes! Do it and end */
  327. /*
  328.     UnLock(lock);
  329.     UnLock(old);
  330. */
  331.     return;
  332.   }
  333.  
  334.                            /* no, dir is a path fragment */
  335.    while (occ--) {
  336.      Entry entry = locateDir(gv, dir, gv -> db, TRUE);
  337.      strcpy(actualdir, entry -> fullPath);
  338.    }
  339.  
  340.    if (actualdir[0]) {
  341.       lock = Lock(actualdir, ACCESS_READ);
  342.       if (lock == 0) {
  343.          fprintf(stderr,
  344.       "*** Couldn't cd to %s, '%s' probably outdated (re-run ccd create)\n",
  345.             actualdir, gv -> prefs.configFile);
  346.          exit(0);
  347.       }
  348.       if (gv -> prefs.noExpand)
  349.     strcpy(actualdir, dir);
  350.       else
  351.     NameFromLock(lock, actualdir, 256);
  352. /*
  353.       getRealName(gv, lock, actualdir);
  354. */
  355.       if (gv -> prefs.verbose)
  356.     printf("Current directory is now %s\n", actualdir);
  357.       CurrentDir(lock);
  358.       SetCurrentDirName(actualdir);
  359. /*
  360.       updatePrompt(actualdir);
  361. */
  362.       unlock(lock);
  363.   }
  364.   else
  365.     printf("%s: no such dir\n", dir);
  366. }
  367.  
  368. void
  369. showAmbiguities(struct GlobalVars *gv, char *dir)
  370. {
  371.   Entry entry;
  372.  
  373.   DB_Rewind(gv -> db);
  374.   while ((entry = locateDir(gv, dir, gv -> db, TRUE))) {
  375.     myputs(entry -> fullPath);
  376.     myputs("\n");
  377.   }
  378.  
  379. }
  380.  
  381. void
  382. nthdir(char *dir, char *path, int n)
  383. /* Path is 'volume:a/b/c' */
  384. /* Return in dir the nth dir in it (e.g. with n=2, it's b) */
  385. {
  386.    char *dirhead = dir;
  387.    while (*path && *path != ':') path++;
  388.    if (*path == ':') {
  389.      path++;
  390.      while (*path) {
  391.          while (*path && *path != '/') {
  392.             *dir++ = *path++;
  393.          }
  394.          if (*(dir - 1) == '\n') dir--;
  395.          *dir++ = '\0';
  396.          if (*path == '/') path++;
  397.          if (n == 1) return;
  398.          else {
  399.             n--; dir = dirhead;
  400.          }
  401.       }
  402.    }
  403.    else {
  404.       printf("nthdir: problem\n");
  405.       exit(0);
  406.    }
  407. }
  408.  
  409.  
  410. static void
  411. freeEntry(Entry entry)
  412. {
  413.   free(entry -> name);
  414. }
  415.  
  416. static int
  417. countCharsInString(char *s, char *set)
  418. /* Return number of occurences of the chars in set in string s */
  419. {
  420.   int result = 0;
  421.   while (*s) {
  422.     if (stpchr(set, *s)) result++;
  423. /*
  424.     char *p = set;
  425.     while (*p) {
  426.       if (*s == *p++) result++;
  427.     }
  428. */
  429.     s++;
  430.   }
  431.  
  432.   return result;
  433. }
  434.  
  435. static int
  436. pathParts(char *dir)
  437. /* Return # of path parts (e.g. ram: -> 1, ram:env -> 2, ram:env/s -> 3 */
  438. {
  439.   int result = countCharsInString(dir, ":/");
  440.   if (strcmp(PathPart(dir), "")) result++;
  441.  
  442.   return result;
  443. }
  444.  
  445. static int
  446. returnNextLine(struct GlobalVars *gv, FILE *f, char *buffer, int size)
  447. {
  448.   int result = 0, i;
  449.  
  450.   if (feof(f))
  451.     result = 0;
  452.   else {
  453.     fgets(buffer, size, f);
  454.     i = strlen(buffer) - 1;
  455.     if (buffer[i] == '\n')
  456.       buffer[i] = '\0';     /* strip trailing \n */
  457.     result = i;
  458.   }
  459.  
  460.   return result;
  461. }
  462.  
  463. static void
  464. buildAntiDataBase(struct GlobalVars *gv)
  465. /* Build the anti database (if the file exists) */
  466. {
  467.   FILE *f = fopen(gv -> prefs.antiConfigFile, "r");
  468.   int l = 1;
  469.   char line[256];
  470.   DataBase db;
  471.   struct _Entry entry;
  472.  
  473.   if (f) {
  474.     gv -> antidb = db = DB_NewDataBase(sizeof(struct _Entry), freeEntry);
  475.     while (returnNextLine(gv, f, line, 256)) {
  476.       entry.spaces = "";
  477.       entry.name = entry.fullPath = strdup(line);
  478.       entry.line = l++;
  479.       DB_AddEntry(db, & entry);
  480.     }
  481.     fclose(f);
  482.   }
  483. }
  484.  
  485. static BOOL
  486. isDirectoryBelow(char *dir1, char *dir2)
  487. /* Is dir2 below dir1? */
  488. {
  489.   BOOL result;
  490.  
  491.   if (strlen(dir1) == 0) result = FALSE;
  492.   else {
  493.     if (strnicmp(dir1, dir2, strlen(dir1)) == 0)
  494.       result = TRUE;
  495.     else
  496.       result = FALSE;
  497.   }
  498.  
  499.   return result;
  500. }
  501.  
  502. static int
  503. buildMainDataBase(struct GlobalVars *gv, DataBase db)
  504. {
  505.   int result = 0, line = 1, n;
  506.   static char onedir[256], spaces[256], fullLine[256];
  507.   FILE *f = fopen(gv -> prefs.configFile, "r");
  508.   struct _Entry entry;
  509.   int i;
  510.  
  511.  
  512.   buildAntiDataBase(gv);
  513.   if (f == NULL) {
  514.     fprintf(stderr, "*** couldn't open '%s'\n", gv -> prefs.configFile);
  515.     result = 1;
  516.   }
  517.   else {
  518.     BOOL skip = NULL;
  519.     while (returnNextLine(gv, f, onedir, 256)) {
  520.       if (gv -> antidb) {
  521.     DataBase adb = gv -> antidb;
  522.     Entry anti;
  523.     DB_Rewind(adb);
  524.     skip = FALSE;
  525.     while (! DB_EndOfDataBase(adb) && ! skip) {
  526.       anti = DB_NextEntry(adb);
  527.       if (isDirectoryBelow(anti -> name, onedir)) {
  528.         skip = TRUE;
  529.       }
  530.     }
  531.       }
  532.  
  533.       if (! skip) {
  534.     n = pathParts(onedir);
  535.     strcpy(spaces, "");
  536.     for (i=0; i<n; i++) strcat(spaces, "  ");
  537.     sprintf(fullLine, "%s%s", spaces, onedir);
  538.     entry.spaces = strdup(spaces);
  539.     entry.fullPath = strdup(onedir);
  540.     entry.name = strdup(n == 1 ? onedir : (char *) FilePart(onedir));
  541.     entry.line = line++;
  542.     DB_AddEntry(db, & entry);
  543.       }
  544.     }
  545.     fclose(f);
  546.   }
  547.  
  548.   return result;
  549.  
  550. }
  551.  
  552. static DataBase
  553. buildAmbiguitiesDataBase(struct GlobalVars *gv, char *dir)
  554. /* Build a database made of ambiguities only */
  555. {
  556.   int line = 1;
  557.   char *fullPath;
  558.   DataBase gdb = gv -> db, result;
  559.   struct _Entry entry, *entry2;
  560.  
  561.   result = DB_NewDataBase(sizeof(struct _Entry), freeEntry);
  562.   DB_Rewind(gdb);
  563.   while (entry2 = locateDir(gv, dir, gdb, TRUE)) {
  564.     entry.fullPath = strdup(entry2 -> fullPath);
  565.     entry.name = entry.fullPath;
  566.     entry.spaces = "";    /* no spaces displayed when displaying ambiguities */
  567.     entry.line = line++;
  568.     DB_AddEntry(result, & entry);
  569.   }
  570.   
  571.   return result;
  572. }
  573.  
  574. void
  575. readEnvironment(struct GlobalVars *gv, int argc, char **argv)
  576. {
  577.    struct CSource cs;
  578.    struct RDArgs rda, *rda2;
  579.    struct Prefs result;
  580.  
  581.    memset(& result, 0, sizeof(result));
  582.    gv -> prefs.configFile = CCD_FILE_DEFAULT_CONFIG;
  583.    gv -> prefs.antiConfigFile = CCD_FILE_DEFAULT_ANTI_CONFIG;
  584.  
  585.    if (argc) {    /* called from CLI */
  586.       memset(& rda, 0, sizeof(rda));
  587.       rda.RDA_ExtHelp = EXTRAHELP;
  588.       rda2 = ReadArgs(gv -> usage, (LONG *) & result, & rda);
  589.       if (rda2 == NULL)
  590.     fprintf(stderr, "Erreur: %s\n", gv -> usage);
  591.  
  592.    /* We have to copy any returned value, its reference is not safe */
  593.       if (result.noExpand) gv -> prefs.noExpand = TRUE;
  594.       if (result.showAmbiguities) gv -> prefs.showAmbiguities = TRUE;
  595.       if (result.update) gv -> prefs.update = TRUE;
  596.       if (result.create) gv -> prefs.create = TRUE;
  597.       if (result.gui) gv -> prefs.gui = TRUE;
  598.       if (result.verbose) gv -> prefs.verbose = TRUE;
  599.       if (result.dirs) {
  600.     char **p = result.dirs;
  601.     int i=0;
  602.     while (p[i]) i++;
  603.     gv -> prefs.dirs = (char **) malloc(sizeof(char *) * i);
  604.     i = 0;
  605.     p = result.dirs;
  606.     while (p[i]) {
  607.       gv -> prefs.dirs[i] = strdup(p[i]); i++;
  608.     }
  609.     gv -> dirCount = i;
  610.  
  611.       }
  612.  
  613.       FreeArgs(rda2);
  614.    }
  615. }
  616.  
  617. int
  618. main(int argc, char **argv)
  619. {
  620.   struct GlobalVars gv;
  621.   struct Prefs prefs;
  622.   int i = 1, line = 1;
  623.   char *dir = NULL;
  624.  
  625.   memset(& gv, 0, sizeof(gv));
  626.   gv.usage = "Create=-c/S,Update=u/S,ConfigFile=-cf/K,AntiConfigFile=-acf/K,Noexpand=-b/S,ShowAmbiguities=-s/S,GUI=-g/S,Verbose=-v/S,Dirs/M";
  627.   gv.actualLine = 1;
  628.  
  629.   readEnvironment(& gv, argc, argv);
  630.   prefs = gv.prefs;
  631.   if (prefs.dirs)
  632.     dir = prefs.dirs[0];
  633.  
  634.   if (prefs.create || prefs.update || prefs.noExpand ||
  635.       prefs.showAmbiguities || prefs.gui || dir) {   /* shortcut */
  636.     gv.db = DB_NewDataBase(sizeof(struct _Entry), freeEntry);
  637.     buildMainDataBase(& gv, gv.db);
  638.   }
  639.  
  640.   if (prefs.gui) {
  641.     char var[128];
  642.     DataBase db = gv.db;
  643.     if (prefs.showAmbiguities && dir) {
  644.       db = buildAmbiguitiesDataBase(& gv, dir);
  645.     }
  646.       
  647.     DeleteVar(CCD_VAR_LAST_DIR, GVF_GLOBAL_ONLY);
  648.     displayTree(& gv, db);
  649.     if (GetVar(CCD_VAR_LAST_DIR, var, 128, GVF_GLOBAL_ONLY) == -1)
  650.       fprintf(stderr, "Aborted\n");
  651.     else {
  652.       changeDir(& gv, var, 1);
  653.     }
  654.   }
  655.  
  656.   else if (prefs.update) {
  657.      updateConfigFile(& gv, prefs.dirs, gv.dirCount);
  658.   }
  659.  
  660.   else if (prefs.create) {
  661.      createConfigFile(& gv, prefs.dirs, gv.dirCount);
  662.   }
  663.  
  664.   else if (prefs.showAmbiguities) {
  665.     char *var = dir;
  666.     if (! var) {
  667.       fprintf(stderr, "You must specify a directory to change to\nAborted\n");
  668.     }
  669.     else {
  670.       int n = 1;
  671.       if (isdigit(prefs.dirs[1][0])) {   /* an occurence was given */
  672.     n = atoi(prefs.dirs[1]);
  673.     changeDir(& gv, var, n);
  674.       }
  675.       else
  676.     showAmbiguities(& gv, var);
  677.     }
  678.   }
  679.   else if (!prefs.gui && dir == NULL) {
  680.     char currentDir[256];
  681.     GetCurrentDirName(currentDir, 256);
  682.     printf("%s\n", currentDir);
  683.   }
  684.   else {
  685.     char *var = dir;
  686.     changeDir(& gv, var, 1);
  687.     if (SetVar(CCD_VAR_LAST_DIR, var, strlen(var), GVF_GLOBAL_ONLY) == 0) {
  688.       BPTR lock;
  689.       lock = CreateDir("ENV:CCD");
  690.       if (SetVar(CCD_VAR_LAST_DIR, var, strlen(var), GVF_GLOBAL_ONLY) == 0) {
  691.     fprintf(stderr, "*** couldn't set variable %s\n", CCD_VAR_LAST_DIR);
  692.       }
  693.     }
  694.   }
  695.   return 0;
  696. }
  697.